
#include "znx_udp_proc.h"
#include "znx_worker_proc.h"


static void
znx_udp_connect_done(znx_conn_t *conn, znx_result_t res,
    znx_udp_connect_handler_pt handler, void *data)
{
    if (res != ZNX_SUCCESS) {
        if (conn->fd != INVALID_FD) {
            znx_conn_close(conn);
        }
        znx_thread_free(conn);
        conn = NULL;
    }

    handler(data, conn, res);
}


void
znx_udp_connect_setup(
    znx_address_t *address,
    znx_address_t *bind_address,
    znx_udp_connect_handler_pt connect_handler,
    void *data)
{
    assert(current_thread_is_worker_thread());

    znx_conn_t *conn = znx_thread_calloc(sizeof(znx_conn_t));
    znx_conn_init(conn, ZNX_CONN_EPOLL_ET);

    znx_syscall_result_t res;
    res = znx_syscall_socket(address->sockaddr.sa_family, SOCK_DGRAM, 0);
    if (res.eno != 0) {
        ZNX_DEF_LOG_ERROR("socket() failed, errno: %d", res.eno);
        znx_udp_connect_done(conn, ZNX_FAILED, connect_handler, data);
        return;
    }
    conn->fd = (int)res.res;

    res = znx_syscall_set_nonblock(conn->fd);
    if (res.eno != 0) {
        ZNX_DEF_LOG_ERROR("setnonblock %d failed, errno: %d", conn->fd, res.eno);
        znx_udp_connect_done(conn, ZNX_FAILED, connect_handler, data);
        return;
    }

    res = znx_syscall_set_close_exec(conn->fd);
    if (res.eno != 0) {
        ZNX_DEF_LOG_ERROR("setcloseexec %d failed, errno: %d", conn->fd, res.eno);
        znx_udp_connect_done(conn, ZNX_FAILED, connect_handler, data);
        return;
    }

    if (bind_address != NULL) {
        res = znx_syscall_bind(conn->fd, &bind_address->sockaddr,
            (socklen_t)znx_address_socklen(bind_address));
        if (res.eno != 0) {
            ZNX_DEF_LOG_ERROR("bind %d local address failed, errno: %d", conn->fd, res.eno);
            znx_udp_connect_done(conn, ZNX_FAILED, connect_handler, data);
            return;
        }
    }

    res = znx_syscall_connect(conn->fd, &address->sockaddr, (socklen_t)znx_address_socklen(address));
    // connect success
    if (res.eno != 0) {
        znx_udp_connect_done(conn, ZNX_FAILED, connect_handler, data);
        return;
    }

    znx_udp_connect_done(conn, ZNX_SUCCESS, connect_handler, data);
}
